#include <p18f25k20.h>
#include <stdlib.h>
#include <string.h>
#include "Delays.h"
#include "Nokia_LCD_controls.h"
#include "globals.h"
#include "SPI_Functions.h"
#include "font 5x8.h"
#include "font 5x7.h"
#include "section_test.h"
#include "Current_time_numbers_framed.h"
#include "Set_time_numbers_framed.h"
#include "Set_time_numbers.h"
#include "fullscreen.h"
#include "Williams_time_numbers.h"
#include "Williams_time_numbers_framed.h"
#include "Williams_start_time_numbers.h"
#include "Williams_start_time_numbers_framed.h"



//--------------------------------------------------------------------------
//		LCD controls
//--------------------------------------------------------------------------



/*
	INPUT: Nothing
	OUTPUT: Nothing
        FUNCTION: Initializes the 84x48 pixel LCD to be ready
                  for accepting commands
*/
void LCD_init(void)
{
    LCD_POWER = 1;      //Turn on the power to the LCD!
                        //  Yes, it's driven by a port pin

    //Reset must go low within 30ms, kept low for some time,
    //  then brought back high.  This initializes all the
    //  internal LCD stuff  -  Added 9/18/2013
    milliseconds(10);
    LCD_RESET = 0;
    milliseconds(20);
    LCD_RESET = 1;

    LCD_CS = 1;         //Bring Chip Select High
    milliseconds(5);
    LCD_RESET = 1;      //Bring Reset high

    milliseconds(10);
    
    
    LCD_write(LCD_COMMAND, 0x21);   //Tell the LCD that extended commands follow
    milliseconds(1);                //Let it settle in...?
    LCD_write(LCD_COMMAND, (LCD_contrast | 0x80));   //Set LCD Vop (Contrast)
    //LCD_write(LCD_COMMAND, 0xBF);   //Set LCD Vop (Contrast)
    //LCD_write(LCD_COMMAND, 0x05);   //Set Temp coefficient to coefficient 1
    LCD_write(LCD_COMMAND, ((LCD_tempco & 0x11) | 0x04));   //Set Temp coefficient
    LCD_write(LCD_COMMAND, (LCD_bias_system | 0x10));   //LCD bias mode 1:48
    //LCD_write(LCD_COMMAND, 0x13);   //LCD bias mode 1:48

    milliseconds(5);
    
    LCD_write(LCD_COMMAND, 0x20);   //Tell the LCD that regular commands follow
    milliseconds(1);                //Let it settle in...?

    if(LCD_invert == 1)
    {
        LCD_write(LCD_COMMAND, 0x0D);   //Set display control
                                        // 0x0C = normal mode
                                        // 0x0D = inverse mode
                                        // 0x09 = all pixels on
    }
    else
    {
        LCD_write(LCD_COMMAND, 0x0C);   //Set display control
                                        // 0x0C = normal mode
                                        // 0x0D = inverse mode
                                        // 0x09 = all pixels on
    }


    milliseconds(10);

    LCD_clear();

    return;
}


/*
    INPUT: Information to write to LCD, flag to indicate data or command
    OUTPUT: Nothing
    FUNCTION: Writes an instruction or data to the LCD
            Sequence is:
            Set LCD Data/Command line to the proper state
            Write the information to the LCD SPI port
 */

void LCD_write(unsigned char write_type, unsigned char data)
{
    if(write_type == LCD_DATA)
    {
        LCD_DC = 1;     //Tell the LCD it will receive data
    }
    else
    {
        LCD_DC = 0;     //Tell the LCD it will receive a command
    }


    SPI_write(data);

    

    return;
}






/*
	INPUT: ASCII character to be printed to LCD
	OUTPUT: Prints the character to the current address of the LCD,
		and increments the current address counter to keep track
                of the y address
*/
void LCD_char(char character)
{
    
    int index=0;

    index = character - 32;

    LCD_write(LCD_DATA, font_5x8[index][0]);
    LCD_write(LCD_DATA, font_5x8[index][1]);
    LCD_write(LCD_DATA, font_5x8[index][2]);
    LCD_write(LCD_DATA, font_5x8[index][3]);
    LCD_write(LCD_DATA, font_5x8[index][4]);

    

    return;
}





/*
    INPUT: Text string to write to the screen, whether to spread
           the string across both chips
    OUTPUT: Nothing
    FUNCTION: Writes the given instruction to the given chip, then
              waits until the LCD no longer returns a busy flag
*/
void LCD_string(char string[])
{
    unsigned char length = 0;
    unsigned char i = 0;
    
    length = strlen(string);
    
    for(i=0; i<length; i++)
    {
        LCD_char(string[i]);        
    }


    return;
}





/*
	INPUT: An LCD DDRAM address. Bit 7 must always be set to 1
	OUTPUT: Changes the display position to the corresponding input address
			and returns the current address
*/
void LCDaddress(unsigned char x_addr, unsigned char y_addr)
{
	

	return;
}




/*
	INPUT: none
	OUTPUT: Clears the entire LCD screen and resets the address to (0,0)
*/
void LCD_clear(void)
{
    int i = 0;

    LCD_write(LCD_COMMAND, LCD_XADDR(0));
    LCD_write(LCD_COMMAND, LCD_YADDR(0));

    //504 is (84*48)/8
    for(i=0; i<504; i++)
    {
        LCD_write(LCD_DATA, 0x00);
    }

    LCD_write(LCD_COMMAND, LCD_XADDR(0));
    LCD_write(LCD_COMMAND, LCD_YADDR(0));


    return;
}



/*
	INPUT: none
	OUTPUT: Clears the given LCD row
*/
void LCD_clear_row(unsigned char row)
{
    unsigned char i = 0;

    LCD_write(LCD_COMMAND, LCD_XADDR(0));
    LCD_write(LCD_COMMAND, LCD_YADDR(row));


    for(i=0; i<84; i++)
    {
        LCD_write(LCD_DATA, 0x00);
    }

    LCD_write(LCD_COMMAND, LCD_XADDR(0));
    LCD_write(LCD_COMMAND, LCD_YADDR(row));


    return;
}


/*
	INPUT: none
	OUTPUT: Clears the LCD screen only in the area 
                inside X start and stop address and Y start and stop address
  
        NOTE:  X addresses and Y addresses are screen addresses according
               to the datasheet
*/
void LCD_partial_clear(unsigned char x_start, unsigned char x_stop, unsigned char y_start, unsigned char y_stop)
{
    int x = 0;
    int y = 0;

    LCD_write(LCD_COMMAND, LCD_XADDR(x_start));
    LCD_write(LCD_COMMAND, LCD_YADDR(y_start));

 
    for(x = x_start; x <= x_stop; x++)
    {
        //always start at the start y address, and
        //  increment the x address
        LCD_write(LCD_COMMAND, LCD_XADDR(x));
        LCD_write(LCD_COMMAND, LCD_YADDR(y_start));

        for(y = y_start; y <= y_stop; y++)
        {
            LCD_write(LCD_DATA, 0x00);
        }
    }

    


    return;
}




/*
	INPUT: none
	OUTPUT: Puts the LCD cursor back to the home position, the upper left most spot
*/
void LCDhome(void)
{
	

	return;
}


void LCD_splashscreen(void)
{
    unsigned char i = 0;
    unsigned char j = 0;

    LCD_write(LCD_COMMAND, LCD_XADDR(0));
    LCD_write(LCD_COMMAND, LCD_YADDR(0));

    for(i=0; i<6; i++)
    {
        for(j=0; j<84; j++)
        {
            LCD_write(LCD_DATA, Williams_screen[i][j]);
        }

    }

    return;
}


///*
// * Draws the data givein in data_array between the x and y addresses
// *   Data array is only 3 by 48, because that will be bigger than the biggest single
// *   character on the screen (the main time)
// */
//void LCD_draw_section(unsigned char data_array[1][3][16], unsigned char x_start, unsigned char x_stop, unsigned char y_start, unsigned char y_stop)
//{
//    unsigned char x = 0;
//    unsigned char y = 0;
//    unsigned char i = 0;
//    unsigned char j = 0;
//
//
//    LCD_write(LCD_COMMAND, LCD_XADDR(x_start));
//    LCD_write(LCD_COMMAND, LCD_YADDR(y_start));
//
//
//    for(y = y_start; y < y_stop; y++)
//    {
//        //always start at the start y address, and
//        //  increment the x address
//        LCD_write(LCD_COMMAND, LCD_YADDR(y));
//        LCD_write(LCD_COMMAND, LCD_XADDR(x_start));
//
//        for(x = x_start; x < x_stop; x++)
//        {
//            LCD_write(LCD_DATA, data_array[i][j]);
//            j++;
//        }
//        i++;
//        j=0;
//    }
//
//
//
//    return;
//}


void LCD_current_time(unsigned char number, unsigned char position, unsigned char command)
{
    unsigned char x_start = 0;
    unsigned char x_stop = 0;
    unsigned char y_start = 0;
    unsigned char y_stop = 0;
    unsigned char x = 0;
    unsigned char y = 0;
    unsigned char i = 0;
    unsigned char j = 0;

    unsigned char shift_value = 0;

    if(hours_24)
    {
        shift_value = CLOCK_24H_SHIFT;
    }
    else
    {
        shift_value = 0;
    }

//    switch(position)
//    {
//        case MIN:
//            x_start = 57;
//            x_stop = 73;
//            y_start = 0;
//            y_stop = 3;
//            break;
//
//        case TMIN:
//            x_start = 42;
//            x_stop = 58;
//            y_start = 0;
//            y_stop = 3;
//            break;
//
//        case HOUR:
//            x_start = 15;
//            x_stop = 31;
//            y_start = 0;
//            y_stop = 3;
//            break;
//
//        case THOUR:
//            x_start = 0;
//            x_stop = 16;
//            y_start = 0;
//            y_stop = 3;
//            break;
//
//    }

    switch(position)
    {
        case MIN:
            x_start = 58 + shift_value;
            x_stop = 75 + shift_value;
            y_start = 0;
            y_stop = 4;
            break;

        case TMIN:
            x_start = 42 + shift_value;
            x_stop = 59 + shift_value;
            y_start = 0;
            y_stop = 4;
            break;

        case HOUR:
            x_start = 16 + shift_value;
            x_stop = 33 + shift_value;
            y_start = 0;
            y_stop = 4;
            break;

        case THOUR:
            x_start = 0 + shift_value;
            x_stop = 17 + shift_value;
            y_start = 0;
            y_stop = 4;
            break;

    }


    LCD_write(LCD_COMMAND, LCD_XADDR(x_start));
    LCD_write(LCD_COMMAND, LCD_YADDR(y_start));


    for(y = y_start; y < y_stop; y++)
    {
        //always start at the start y address, and
        //  increment the x address
        LCD_write(LCD_COMMAND, LCD_YADDR(y));
        LCD_write(LCD_COMMAND, LCD_XADDR(x_start));

        for(x = x_start; x < x_stop; x++)
        {
            if(command)
            {
                LCD_write(LCD_DATA, Williams_time_numbers[number][i][j]);
            }
            else
            {
                LCD_write(LCD_DATA, 0x00);
            }
            j++;
        }
        i++;
        j=0;
    }


    
    return;
}




void LCD_runtime_mins(unsigned char number, unsigned char position, unsigned char command)
{
    unsigned char x_start = 0;
    unsigned char x_stop = 0;
    unsigned char y_start = 0;
    unsigned char y_stop = 0;
    unsigned char x = 0;
    unsigned char y = 0;
    unsigned char i = 0;
    unsigned char j = 0;

    switch(position)
    {
        
        case MIN:
            x_start = 11;
            x_stop = 22;
            y_start = 4;
            y_stop = 5;
            break;

        case TMIN:
            x_start = 0;
            x_stop = 12;
            y_start = 4;
            y_stop = 5;
            break;

    }


    LCD_write(LCD_COMMAND, LCD_XADDR(x_start));
    LCD_write(LCD_COMMAND, LCD_YADDR(y_start));


    for(y = y_start; y <= y_stop; y++)
    {
        //always start at the start y address, and
        //  increment the x address
        LCD_write(LCD_COMMAND, LCD_YADDR(y));
        LCD_write(LCD_COMMAND, LCD_XADDR(x_start));

        for(x = x_start; x <= x_stop; x++)
        {
            if(command)
            {
                LCD_write(LCD_DATA, Williams_start_time_numbers[number][i][j]);
            }
            else
            {
                LCD_write(LCD_DATA, 0x00);
            }
            j++;
        }
        i++;
        j=0;
    }



    return;
}





void LCD_start_time(unsigned char number, unsigned char position, unsigned char command)
{
    unsigned char x_start = 0;
    unsigned char x_stop = 0;
    unsigned char y_start = 0;
    unsigned char y_stop = 0;
    unsigned char x = 0;
    unsigned char y = 0;
    unsigned char i = 0;
    unsigned char j = 0;

    unsigned char shift_value = 0;
    
    if(hours_24)
    {
        shift_value = START_TIME_24H_SHIFT;
    }
    else
    {
        shift_value = 0;
    }



    switch(position)
    {
        case MIN:
            x_start = 63 + shift_value;
            x_stop = 74 + shift_value;
            y_start = 4;
            y_stop = 5;
            break;

        case TMIN:
            x_start = 52 + shift_value;
            x_stop = 63 + shift_value;
            y_start = 4;
            y_stop = 5;
            break;

        case HOUR:
            x_start = 36 + shift_value;
            x_stop = 47 + shift_value;
            y_start = 4;
            y_stop = 5;
            break;

        case THOUR:
            x_start = 25 + shift_value;
            x_stop = 36 + shift_value;
            y_start = 4;
            y_stop = 5;
            break;

    }


    LCD_write(LCD_COMMAND, LCD_XADDR(x_start));
    LCD_write(LCD_COMMAND, LCD_YADDR(y_start));


    for(y = y_start; y <= y_stop; y++)
    {
        //always start at the start y address, and
        //  increment the x address
        LCD_write(LCD_COMMAND, LCD_YADDR(y));
        LCD_write(LCD_COMMAND, LCD_XADDR(x_start));

        for(x = x_start; x <= x_stop; x++)
        {
            if(command)
            {
                LCD_write(LCD_DATA, Williams_start_time_numbers[number][i][j]);
            }
            else
            {
                LCD_write(LCD_DATA, 0x00);
            }
            j++;
        }
        i++;
        j=0;
    }

    /* draw the colon */
    LCD_write(LCD_COMMAND, LCD_YADDR(4));
    LCD_write(LCD_COMMAND, LCD_XADDR(49 + shift_value));

    LCD_write(LCD_DATA, 0x60);
    LCD_write(LCD_DATA, 0x60);

    /* draw the colon */
    LCD_write(LCD_COMMAND, LCD_YADDR(5));
    LCD_write(LCD_COMMAND, LCD_XADDR(49 + shift_value));

    LCD_write(LCD_DATA, 0x06);
    LCD_write(LCD_DATA, 0x06);


    /* draw the AM or PM */
    if(!hours_24)
    {
        LCD_start_am_pm();
    }


    return;
}





void LCD_am_pm(void)
{
    LCD_write(LCD_COMMAND, LCD_YADDR(2));
    LCD_write(LCD_COMMAND, LCD_XADDR(74));

    if(am_pm)
    {
        LCD_write(LCD_DATA, 0xF8);
        LCD_write(LCD_DATA, 0x48);
        LCD_write(LCD_DATA, 0x48);
        LCD_write(LCD_DATA, 0x30);
        LCD_write(LCD_DATA, 0x00);
        LCD_write(LCD_DATA, 0xF8);
        LCD_write(LCD_DATA, 0x10);
        LCD_write(LCD_DATA, 0x20);
        LCD_write(LCD_DATA, 0x10);
        LCD_write(LCD_DATA, 0xF8);

        LCD_write(LCD_COMMAND, LCD_YADDR(3));
        LCD_write(LCD_COMMAND, LCD_XADDR(74));

        LCD_write(LCD_DATA, 0x01);

        LCD_write(LCD_COMMAND, LCD_XADDR(77));

        LCD_write(LCD_DATA, 0x00);

        LCD_write(LCD_COMMAND, LCD_XADDR(79));

        LCD_write(LCD_DATA, 0x01);

        LCD_write(LCD_COMMAND, LCD_XADDR(83));

        LCD_write(LCD_DATA, 0x01);

        

//        LCD_write(LCD_DATA, 0xFF);
//        LCD_write(LCD_DATA, 0x11);
//        LCD_write(LCD_DATA, 0x11);
//        LCD_write(LCD_DATA, 0x0E);
//        LCD_write(LCD_DATA, 0x00);
//        LCD_write(LCD_DATA, 0xFF);
//        LCD_write(LCD_DATA, 0x02);
//        LCD_write(LCD_DATA, 0x04);
//        LCD_write(LCD_DATA, 0x02);
//        LCD_write(LCD_DATA, 0xFF);
    }
    else
    {
        LCD_write(LCD_DATA, 0xF0);
        LCD_write(LCD_DATA, 0x48);
        LCD_write(LCD_DATA, 0x48);
        LCD_write(LCD_DATA, 0xF0);
        LCD_write(LCD_DATA, 0x00);
        LCD_write(LCD_DATA, 0xF8);
        LCD_write(LCD_DATA, 0x10);
        LCD_write(LCD_DATA, 0x20);
        LCD_write(LCD_DATA, 0x10);
        LCD_write(LCD_DATA, 0xF8);

        LCD_write(LCD_COMMAND, LCD_YADDR(3));
        LCD_write(LCD_COMMAND, LCD_XADDR(74));

        LCD_write(LCD_DATA, 0x01);

        LCD_write(LCD_COMMAND, LCD_XADDR(77));

        LCD_write(LCD_DATA, 0x01);

        LCD_write(LCD_COMMAND, LCD_XADDR(79));

        LCD_write(LCD_DATA, 0x01);

        LCD_write(LCD_COMMAND, LCD_XADDR(83));

        LCD_write(LCD_DATA, 0x01);

//        LCD_write(LCD_DATA, 0xFE);
//        LCD_write(LCD_DATA, 0x11);
//        LCD_write(LCD_DATA, 0x11);
//        LCD_write(LCD_DATA, 0xFE);
//        LCD_write(LCD_DATA, 0x00);
//        LCD_write(LCD_DATA, 0xFF);
//        LCD_write(LCD_DATA, 0x02);
//        LCD_write(LCD_DATA, 0x04);
//        LCD_write(LCD_DATA, 0x02);
//        LCD_write(LCD_DATA, 0xFF);
    }


    return;
}




void LCD_start_am_pm(void)
{
    LCD_write(LCD_COMMAND, LCD_YADDR(5));
    LCD_write(LCD_COMMAND, LCD_XADDR(74));

    if(start_am_pm)
    {
        LCD_write(LCD_DATA, 0xFC);
        LCD_write(LCD_DATA, 0x24);
        LCD_write(LCD_DATA, 0x24);
        LCD_write(LCD_DATA, 0x18);
        LCD_write(LCD_DATA, 0x00);
        LCD_write(LCD_DATA, 0xFC);
        LCD_write(LCD_DATA, 0x08);
        LCD_write(LCD_DATA, 0x10);
        LCD_write(LCD_DATA, 0x08);
        LCD_write(LCD_DATA, 0xFC);

//        LCD_write(LCD_DATA, 0xFC);
//        LCD_write(LCD_DATA, 0x24);
//        LCD_write(LCD_DATA, 0x24);
//        LCD_write(LCD_DATA, 0x18);
//        LCD_write(LCD_DATA, 0x00);
//        LCD_write(LCD_DATA, 0xFC);
//        LCD_write(LCD_DATA, 0x08);
//        LCD_write(LCD_DATA, 0x10);
//        LCD_write(LCD_DATA, 0x08);
//        LCD_write(LCD_DATA, 0xFC);
    }
    else
    {
        LCD_write(LCD_DATA, 0xF8);
        LCD_write(LCD_DATA, 0x24);
        LCD_write(LCD_DATA, 0x24);
        LCD_write(LCD_DATA, 0xF8);
        LCD_write(LCD_DATA, 0x00);
        LCD_write(LCD_DATA, 0xFC);
        LCD_write(LCD_DATA, 0x08);
        LCD_write(LCD_DATA, 0x10);
        LCD_write(LCD_DATA, 0x08);
        LCD_write(LCD_DATA, 0xFC);
        
//        LCD_write(LCD_DATA, 0xF8);
//        LCD_write(LCD_DATA, 0x24);
//        LCD_write(LCD_DATA, 0x24);
//        LCD_write(LCD_DATA, 0xF8);
//        LCD_write(LCD_DATA, 0x00);
//        LCD_write(LCD_DATA, 0xFC);
//        LCD_write(LCD_DATA, 0x08);
//        LCD_write(LCD_DATA, 0x10);
//        LCD_write(LCD_DATA, 0x08);
//        LCD_write(LCD_DATA, 0xFC);
    }

    return;
}


void LCD_draw_frame(void)
{
    unsigned char i = 0;
    unsigned char data = 0;

    unsigned char shift_value = 0;

    if(hours_24)
    {
        shift_value = CLOCK_24H_SHIFT;
    }
    else
    {
        shift_value = 0;
    }


    LCD_write(LCD_COMMAND, LCD_YADDR(3));
    LCD_write(LCD_COMMAND, LCD_XADDR(0));

    for(i=0; i<84; i++)
    {
        if(i<shift_value)
        {
            data = 0x30;
        }
        else if(i<(17+shift_value))
        {
            if(current_ten_hours == 0)
            {
                data = 0x30;
            }
            else
            {
                data = Williams_time_numbers[current_ten_hours][3][i-shift_value] | 0x30;
            }
        }
        else if(i<(33+shift_value))
        {
            data = Williams_time_numbers[current_hours][3][i-16-shift_value] | 0x30;
        }
        else if(i<(42+shift_value))
        {
            data = 0x30;
        }
        else if(i<(59+shift_value))
        {
            data = Williams_time_numbers[current_ten_minutes][3][i-42-shift_value] | 0x30;
        }
        else if(i<(74+shift_value))
        {
            data = Williams_time_numbers[current_minutes][3][i-58-shift_value] | 0x30;
        }
        else if(i==(74+shift_value))
        {
            data = 0x30 | (0x01 & !hours_24);
        }
        else if(i==(77+shift_value))
        {
                if(am_pm)
                {
                    data = 0x30;
                }
                else
                {
                    data = 0x30 | (0x01 & !hours_24);
                }
            
        }
        else if(i==(79+shift_value))
        {
            data = 0x30 | (0x01 & !hours_24);
        }
        else if(i==(83+shift_value))
        {
            data = 0x30 | (0x01 & !hours_24);
        }
        else
        {
            data = 0x30;
        }
        
        LCD_write(LCD_DATA, data);
    }


    return;
}